package com.app.core.mybatis;

import cn.hutool.core.convert.Convert;
import com.app.core.mvc.serialization.EnumDefinition;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/**
 * @author qiangt
 * @version 1.0
 * @date 2023/10/17 16:39
 * @description: mybatis自定义的枚举TypeHandler
 * 注意：枚举是单独使用mybatis plus的配置mybatis-plus.configuration.default-enum-type-handler，
 * 不用放入package com.app.core.mybatis.handler，放入启动报错
 */
public class CustomizeEnumTypeHandler<E extends Enum<E> & EnumDefinition> extends BaseTypeHandler<E> {

    private final Class<E> type;
    private final boolean isEnumDefinition;

    public CustomizeEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        this.isEnumDefinition = isEnumDefinition(type);
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {
        if (this.isEnumDefinition) {
            enumDefinitionToDB(preparedStatement, i, e);
        } else {
            enumToDB(preparedStatement, i, e);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object s = rs.getObject(columnName);
        return s == null ? null : this.isEnumDefinition ? dbToEnumDefinition(s) : dbToEnum(s);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object s = rs.getObject(columnIndex);
        return s == null ? null : this.isEnumDefinition ? dbToEnumDefinition(s) : dbToEnum(s);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object s = cs.getObject(columnIndex);
        return s == null ? null : this.isEnumDefinition ? dbToEnumDefinition(s) : dbToEnum(s);
    }

    private boolean isEnumDefinition(Class<E> type) {
        // 获取类实现的所有接口
        Class<?>[] interfaces = type.getInterfaces();
        // 检查这些接口中是否包含指定的接口
        for (Class<?> iface : interfaces) {
            if (iface.equals(EnumDefinition.class)) {
                return true;
            }
        }
        return false;
    }

    private void enumToDB(PreparedStatement preparedStatement, int i, E e) throws SQLException {
        preparedStatement.setString(i, e.name());
    }

    private void enumDefinitionToDB(PreparedStatement preparedStatement, int i, E e) throws SQLException {
        if (e.getCode().getClass() == String.class) {
            preparedStatement.setString(i, Convert.toStr(e.getCode()));
        } else {
            preparedStatement.setInt(i, Convert.toInt(e.getCode()));
        }
    }

    private E dbToEnumDefinition(Object value) {
        try {
            Method getCode = type.getMethod("getCode");
            for (E e : type.getEnumConstants()) {
                Object code = getCode.invoke(e);
                if (Objects.equals(code.toString(), this.parseDataType(value))) {
                    return e;
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        return null;
    }

    private E dbToEnum(Object value) {
        try {
            for (E e : type.getEnumConstants()) {
                if (Objects.equals(e.name(), this.parseDataType(value))) {
                    return e;
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        return null;
    }

    private String parseDataType(Object value) {
        if (value instanceof Boolean) {
            return String.valueOf((Boolean) value ? 1 : 0);
        } else {
            return value.toString();
        }
    }
}
